package py.una.pol.ii.is2.facade.sistema;

import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.Vector;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import py.una.pol.ii.is2.dao.sistema.ModuloDAO;
import py.una.pol.ii.is2.dao.sistema.PantallaDAO;
import py.una.pol.ii.is2.dao.sistema.UsuarioDAOLocal;
import py.una.pol.ii.is2.domain.sistema.Modulo;
import py.una.pol.ii.is2.domain.sistema.Pantalla;
import py.una.pol.ii.is2.domain.sistema.Rol;
import py.una.pol.ii.is2.domain.sistema.Usuario;

/**
 * Clase que implementa la interfaz AccesoConAutenticacionFacade
 * @author mbenitez
 * @date 02/05/2009
 */
@Stateless
public class AccesoConAutenticacionFacadeBean implements AccesoConAutenticacionFacade {
    /**
     * Manejador de instancias de entidades para recuperar instancias desde
     * el Persistence Context
     */
    @PersistenceContext
    private EntityManager em;
    @EJB
    private UsuarioDAOLocal usuarioDao;
    @EJB
    private ModuloDAO moduloDao;
    @EJB
    private PantallaDAO pantallaDao;
    

    /**
     * Se verifica la existencia del usuario. Luego se autentica por medio de
     * la contraseña ingresada desde el login.
     *
     * @param username Nombre de usuario
     * @param password Contraseña de usuario
     * @return usuario Una instancia del usuario en caso de que la autenticación sea exitosa, en otro caso null.
     * @throws java.lang.Exception
     */
    public Usuario autenticarUsuario(String username, String password) throws Exception {
        Usuario usuario = null;
        try {
            usuario = usuarioDao.find(username);
            if (!usuario.getUsuaContrasena().equals(password)) {
                usuario = null;
            }
        } catch (Exception e) {
            throw e;
        } finally {
            return usuario;
        }
    }

    /**
     * Retorna los módulos a los que el usuario tiene acceso.
     *
     * @param usuario Usuario autenticado
     * @return listaModulos Módulos accesibles por el usuario
     * @throws java.lang.Exception
     */
    public List<Modulo> getListaModulos(Usuario usuario) throws Exception {
        List<Modulo> listaModulos = null;
        // esta consulta se tiene que pasar a JPQL
        String sql = " select distinct mod.modu_cod from usuario_rol " +
                " inner join rol on usro_rol = rol_cod " +
                " inner join pant_rol pr on paro_rol = rol_cod " +
                " inner join pantalla p on paro_pantalla = pant_nombre " +
                " inner join modulo mod on pant_modulo = modu_cod " +
                " where usro_usuario = '" + usuario.getUsuaAlias() + "'";

        javax.persistence.Query q = em.createNativeQuery(sql);
//        q.setParameter("usuario", usuario.getUsuaAlias());
        try {
            listaModulos = (List<Modulo>) q.getResultList();
        } catch (Exception e) {
        } finally {
            return listaModulos;
        }
    }

    /**
     * Retorna las pantallas a las que el usuario tiene acceso, dentro del módulo.
     *
     * @param usuario Usuario autenticado
     * @param modulo Módulo accesible por el usuario
     * @return listaPantallas Pantallas correspondientes al módulo y al usuario, en otro caso null.
     * @throws java.lang.Exception
     */
     public List<Pantalla> getListaPantallas(Usuario usuario, Modulo modulo) throws Exception {
        List<Pantalla> listaPantallas = null;
        // esta consulta se tiene que pasar a JPQL
        String sql = " select distinct pant_nombre from usuario_rol " +
                " inner join rol on usro_rol = rol_cod " +
                " inner join pant_rol pr on paro_rol = rol_cod " +
                " inner join pantalla p on paro_pantalla = pant_nombre " +
                " inner join modulo mod on pant_modulo = modu_cod " +
                " where usro_usuario = '" + usuario.getUsuaAlias() + "'" +
                " and modu_cod =  '" + modulo.getModuCod() + "'";

        javax.persistence.Query q = em.createQuery(sql);
//        q.setParameter("usuario", usuario);
//        q.setParameter("modulo", modulo);
        try {
            listaPantallas = q.getResultList();
        } catch (Exception e) {
        } finally {
            return listaPantallas;
        }
    }

    /**
     *
     * Devuelve la lista de módulos a las que el usuario tiene acceso.
     *
     * @param usuario Usuario autenticado
     * @return listaModulos Módulos accesibles por el usuario
     * @throws java.lang.Exception
     */
    public List<Modulo> getListaCodigoModulos(Usuario usuario) throws Exception {
        List<Modulo> listaModulos = new ArrayList<Modulo>();
        List<Vector> listaModulosPrevia = null;
        // esta consulta se tiene que pasar a JPQL
        String sql = " select distinct mod.modu_cod from usuario_rol " +
                " inner join rol on usro_rol = rol_cod " +
                " inner join pant_rol pr on paro_rol = rol_cod " +
                " inner join pantalla p on paro_pantalla = pant_nombre " +
                " inner join modulo mod on pant_modulo = modu_cod " +
                " where usro_usuario = '" + usuario.getUsuaAlias() + "' order by mod.modu_cod";

        javax.persistence.Query q = em.createNativeQuery(sql);
//        q.setParameter("usuario", usuario.getUsuaAlias());
        try {
            listaModulosPrevia = q.getResultList();
            for (Vector objeto : listaModulosPrevia) {
                String codigo = objeto.get(0).toString();
                listaModulos.add(moduloDao.find(codigo));
            }
        } catch (Exception e) {
            throw e;
        } finally {
            return listaModulos;
        }
    }

    /**
     *
     * Devuelve la lista de las pantallas a las que el usuario tiene acceso
     * en el módulo recibido, no devuelve repetidos.
     *
     * @param usuario Usuario autenticado
     * @param modulo Módulo accesible por el usuario
     * @return listaPantallas Pantallas correspondientes al módulo y al usuario, en otro caso null.
     * @throws java.lang.Exception
     */
    public List<Pantalla> getListaCodigoPantallas(Usuario usuario, Modulo modulo) throws Exception {
        List<Vector> listaPantallasPrevia = null;
        List<Pantalla> listaPantalla = new ArrayList<Pantalla>();
        // esta consulta se tiene que pasar a JPQL
        String sql = " select distinct pant_nombre from usuario_rol " +
                " inner join rol on usro_rol = rol_cod " +
                " inner join pant_rol pr on paro_rol = rol_cod " +
                " inner join pantalla p on paro_pantalla = pant_nombre " +
                " inner join modulo mod on pant_modulo = modu_cod " +
                " where usro_usuario = '" + usuario.getUsuaAlias() + "'" +
                " and modu_cod =  '" + modulo.getModuCod() + "' order by pant_nombre" ;

        javax.persistence.Query q = em.createNativeQuery(sql);
//        q.setParameter("usuario", usuario);
//        q.setParameter("modulo", modulo);
        try {
            listaPantallasPrevia = q.getResultList();
            for (Vector objeto : listaPantallasPrevia) {
                String codigo = objeto.get(0).toString();
                listaPantalla.add(pantallaDao.find(codigo));
            }
        } catch (Exception e) {
            throw e;
        } finally {
            return listaPantalla;
        }
    }

    /**
     *
     * Devuelve un lista de parejas <pantalla, nivelAcceso>, que permite identificar
     * de manera segura el nivel de acceso de este usuario, para la pantalla dada.
     * Devuelve el mayor nivel de acceso para la pantalla.
     *
     * @param usuario Usuario autenticado
     * @return listaPantallaPermiso Lista de pantalla y nivel de acceso para el usuario correspondiente
     * @throws java.lang.Exception
     */
     public List<String> getListaPantallaPermisos(Usuario usuario) throws Exception{
        List<String> listaPantallaPermiso = null;
        List<Vector> listaResultadoPrevia = null;
        try {// esta consulta se tiene que pasar a JPQL
            for (Rol rol : usuario.getRolCollection()) {
                String sql = " select distinct pr.paro_pantalla, pr.paro_acceso from usuario_rol " +
                        " inner join rol on usro_rol = rol_cod " +
                        " inner join pant_rol pr on paro_rol = rol_cod " +
                        " inner join pantalla p on paro_pantalla = pant_nombre " +
                        " inner join modulo mod on pant_modulo = modu_cod " +
                        " where usro_usuario = '" + usuario.getUsuaAlias() + "'" +
                        " and rol_cod = " + rol.getRolCod().toString();
                
                javax.persistence.Query q = em.createNativeQuery(sql);

                listaResultadoPrevia = q.getResultList();
                listaPantallaPermiso = new ArrayList<String>();
                for (Vector objeto : listaResultadoPrevia) {
                    String nombrePantalla = objeto.get(0).toString();
                    String acceso = objeto.get(1).toString();
                    if (checkPantalla(nombrePantalla, acceso, listaPantallaPermiso)) {
                        listaPantallaPermiso.add(nombrePantalla + "-" + acceso);
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            return listaPantallaPermiso;
        }
    }
    /**
     *
     * Chequea que no exista la pantalla en la lista. De ser asi se debe reemplazar.
     *
     * @param pantalla
     * @param acceso
     * @param lista
     * @return
     */
    private boolean checkPantalla(String pantalla, String acceso, List<String> lista) {

        boolean retorno = true;
        int i = 0;
        StringTokenizer token;
        for (String item : lista) {
            token = new StringTokenizer(item, "-");
            String nombrePantalla = token.nextToken();
            String nivelAcceso = token.nextToken();
            // filtramos el nombre de la pantalla y el nivel de mayor nivel de acceso
            if (pantalla.equals(nombrePantalla) && Integer.parseInt(acceso) > Integer.parseInt(nivelAcceso)) {
                retorno = false;
                // removemos el item para que no existan repetidos
                lista.remove(item);
                break;
            }
            i++;
        }
        return retorno;
    }
}
